﻿using CubeCube.Tile;
using CubeCube.Utils;
using CubeCube.World;
using System;
using System.Collections.Generic;
using System.IO;

namespace CubeCube.Sector
{
    public class SectorManager
    {
        public CubeWorld world;
        public Chunk.Chunk chunk;

        public int xSectors, ySectors, zSectors;
        public int xSectorsBits, ySectorsBits, zSectorsBits, xySectorsBits;

        public Sector[] sectors;

        private bool pendingSectorsUpdateOrderValid = false;   //sector更新队列是否已经排序了
        private List<Sector> pendingSectorsUpdate = new List<Sector>();
        private bool pendingSectorsUpdateLightOrderValid = false; //light更新队列是否已经排序了
        private List<Sector> pendingSectorsUpdateLight = new List<Sector>();

        public SectorManager(CubeWorld cubeWorld, Chunk.Chunk chunk)
        {
            this.world = cubeWorld;
            this.chunk = chunk;
            xSectorsBits = chunk.tileManager.sizeXbits - Graphics.SECTOR_SIZE_BITS;
            ySectorsBits = chunk.tileManager.sizeYbits - Graphics.SECTOR_SIZE_BITS;
            zSectorsBits = chunk.tileManager.sizeZbits - Graphics.SECTOR_SIZE_BITS;

            xySectorsBits = xSectorsBits + ySectorsBits;

            xSectors = 1 << xSectorsBits;
            ySectors = 1 << ySectorsBits;
            zSectors = 1 << zSectorsBits;
        }

        public void Create()
        {
            sectors = new Sector[xSectors * ySectors * zSectors];
            for (int x = 0; x < xSectors; x++)
                for (int z = 0; z < zSectors; z++)
                    for (int y = ySectors - 1; y >= 0; y--)
                        CreateSector(new IndexPosition(x, y, z), false);
        }

        public void Save(BinaryWriter bw)
        {

        }

        public void Load(BinaryReader br)
        {
            pendingSectorsUpdateOrderValid = false;
            pendingSectorsUpdateLightOrderValid = false;
            pendingSectorsUpdate.Clear();
            pendingSectorsUpdateLight.Clear();

            for (int x = 0; x < xSectors; x++)
                for (int z = 0; z < zSectors; z++)
                    for (int y = ySectors - 1; y >= 0; y--)
                    {
                        Sector s = sectors[x | (y << xSectorsBits) | (z << xySectorsBits)];
                        s.insideInvalidateLightQueue = false;
                        s.insideInvalidateSectorQueue = false; //TODO : 看是否需要設置為true
                        pendingSectorsUpdate.Add(s);
                    }
        }


        //创建扇区
        private void CreateSector(IndexPosition posSector, bool inmediate)
        {
            IndexPosition sectorOffset = new IndexPosition(posSector.x << Graphics.SECTOR_SIZE_BITS, posSector.y << Graphics.SECTOR_SIZE_BITS, posSector.z << Graphics.SECTOR_SIZE_BITS);

            Sector sector = new Sector(world, posSector, sectorOffset);

            sectors[posSector.x | (posSector.y << xSectorsBits) | (posSector.z << xySectorsBits)] = sector;

            if (inmediate == false)
                pendingSectorsUpdate.Add(sector);
            else
                sector.UpdateMesh();
        }

        //通过tilepos去获取所属的扇区
        public Sector GetSectorTile(IndexPosition pos)
        {
            if (!chunk.ContainTilePosition(pos))
            {
                return null;
            }
            IndexPosition offset = pos - chunk.tileOffset;

            offset.x >>= Graphics.SECTOR_SIZE_BITS;
            offset.y >>= Graphics.SECTOR_SIZE_BITS;
            offset.z >>= Graphics.SECTOR_SIZE_BITS;

            return sectors[offset.x | (offset.y << xSectorsBits) | (offset.z << xySectorsBits)];
        }

        //直接根据sector索引坐标获取sector
        public Sector GetSector(IndexPosition posSector)
        {
            return sectors[posSector.x | (posSector.y << xSectorsBits) | (posSector.z << xySectorsBits)];
        }

        //根据该点更新扇区
        public void TileInvalidated(IndexPosition pos)
        {
            if (!chunk.ContainTilePosition(pos))
            {
                return;
            }
            IndexPosition offset = pos - chunk.tileOffset;

            int xSector = offset.x >> Graphics.SECTOR_SIZE_BITS;
            int ySector = offset.y >> Graphics.SECTOR_SIZE_BITS;
            int zSector = offset.z >> Graphics.SECTOR_SIZE_BITS;

            if (!EnqueueInvalidatedSector(xSector, ySector, zSector))
            {
                return;
            }

            //通知临界更新
            if (offset.x % Graphics.SECTOR_SIZE == 0)
            {
                if (xSector == 0)
                {
                    Chunk.Chunk nearChunk = chunk.GetNearChunck(Graphics.Faces.Left);
                    if(nearChunk != null)
                    {
                        nearChunk.sectorManager.EnqueueInvalidatedSector(xSectors - 1, ySector, zSector);
                    }
                }
                else
                {
                    EnqueueInvalidatedSector(xSector - 1, ySector, zSector);
                }
            }
            else if (offset.x % Graphics.SECTOR_SIZE == Graphics.SECTOR_SIZE - 1)
            {
                if (xSector == xSectors - 1)
                {
                    Chunk.Chunk nearChunk = chunk.GetNearChunck(Graphics.Faces.Right);
                    if (nearChunk != null)
                    {
                        nearChunk.sectorManager.EnqueueInvalidatedSector(0, ySector, zSector);
                    }
                }
                else
                {
                    EnqueueInvalidatedSector(xSector + 1, ySector, zSector);
                }
            }

            if (offset.y % Graphics.SECTOR_SIZE == 0)
            {
                if (ySector == 0)
                {
                    Chunk.Chunk nearChunk = chunk.GetNearChunck(Graphics.Faces.Down);
                    if (nearChunk != null)
                    {
                        nearChunk.sectorManager.EnqueueInvalidatedSector(xSectors, ySectors - 1, zSector);
                    }
                }
                else
                {
                    EnqueueInvalidatedSector(xSector, ySector - 1, zSector);
                }
            }
            else if (offset.y % Graphics.SECTOR_SIZE == Graphics.SECTOR_SIZE - 1)
            {
                if (ySector == ySectors - 1)
                {
                    Chunk.Chunk nearChunk = chunk.GetNearChunck(Graphics.Faces.Up);
                    if (nearChunk != null)
                    {
                        nearChunk.sectorManager.EnqueueInvalidatedSector(xSectors, 0, zSector);
                    }
                }
                else
                {
                    EnqueueInvalidatedSector(xSector, ySector + 1, zSector);
                }
            }

            if (offset.z % Graphics.SECTOR_SIZE == 0)
            {
                if (zSector == 0)
                {
                    Chunk.Chunk nearChunk = chunk.GetNearChunck(Graphics.Faces.Back);
                    if (nearChunk != null)
                    {
                        nearChunk.sectorManager.EnqueueInvalidatedSector(xSectors, ySector, zSectors - 1);
                    }
                }
                else
                {
                    EnqueueInvalidatedSector(xSector, ySector, zSector - 1);
                }
            }
            else if (offset.z % Graphics.SECTOR_SIZE == Graphics.SECTOR_SIZE - 1)
            {
                if (zSector == zSectors - 1)
                {
                    Chunk.Chunk nearChunk = chunk.GetNearChunck(Graphics.Faces.Front);
                    if (nearChunk != null)
                    {
                        nearChunk.sectorManager.EnqueueInvalidatedSector(xSectors, ySector, 0);
                    }
                }
                else
                {
                    EnqueueInvalidatedSector(xSector, ySector, zSector + 1);
                }
            }
        }


        //入队一个sector更新
        public bool EnqueueInvalidatedSector(int xSector, int ySector, int zSector)
        {
            Sector sector = sectors[xSector | (ySector << xSectorsBits) | (zSector << xySectorsBits)];
            //如果没有在sector更新队列中，加入sector更新队列
            if (sector.insideInvalidateSectorQueue == false)
            {
                pendingSectorsUpdate.Add(sector);
                pendingSectorsUpdateOrderValid = false;

                sector.insideInvalidateSectorQueue = true;
                //如果在light更新队列中，就要移除
                if (sector.insideInvalidateLightQueue)
                {
                    sector.insideInvalidateLightQueue = false;
                    pendingSectorsUpdateLight.Remove(sector);
                }
                return true;
            }
            return false;
        }

        //入队一个light更新
        public void EnqueueInvalidatedLight(int xSector, int ySector, int zSector)
        {
            Sector sector = sectors[xSector | (ySector << xSectorsBits) | (zSector << xySectorsBits)];

            if (sector.insideInvalidateLightQueue == false && sector.insideInvalidateSectorQueue == false)
            {
                pendingSectorsUpdateLight.Add(sector);
                pendingSectorsUpdateLightOrderValid = false;

                sector.insideInvalidateLightQueue = true;
            }
        }

        static private IndexPosition currentPlayerPositionForSort;
        public void Update(float deltaTime)
        {
            long start = DateTime.Now.Ticks;
            long ticksInMillisecond = 10000;

            currentPlayerPositionForSort = Graphics.Vector3ToTileIndexPosition(chunk.world.PlayerPosition);

            //排序
            if (pendingSectorsUpdateOrderValid == false)
            {
                pendingSectorsUpdate.Sort(CompareSectorsByDistanceToPlayer);
                pendingSectorsUpdateOrderValid = true;
            }

            if (pendingSectorsUpdateLightOrderValid == false)
            {
                pendingSectorsUpdateLight.Sort(CompareSectorsByDistanceToPlayer);
                pendingSectorsUpdateLightOrderValid = true;
            }

            int updateRounds = 0;

            //Always update at leat 8 sectors (they could be the 8 sectors nearest to the player)
            //在时间范围内(0.01s),总是更新8个扇区（它们可能是距离玩家最近的8个扇区）
            while ((pendingSectorsUpdate.Count > 0 || pendingSectorsUpdateLight.Count > 0) &&
                (DateTime.Now.Ticks - start < ticksInMillisecond * 10 || updateRounds < 8))
            {
                if (pendingSectorsUpdate.Count > 0)
                {
                    Sector sectorToUpdate = pendingSectorsUpdate[pendingSectorsUpdate.Count - 1];
                    pendingSectorsUpdate.RemoveAt(pendingSectorsUpdate.Count - 1);
                    sectorToUpdate.UpdateMesh();
                    sectorToUpdate.insideInvalidateSectorQueue = false;
                }

                if (pendingSectorsUpdateLight.Count > 0)
                {
                    Sector sectorToUpdate = pendingSectorsUpdateLight[pendingSectorsUpdateLight.Count - 1];
                    pendingSectorsUpdateLight.RemoveAt(pendingSectorsUpdateLight.Count - 1);
                    sectorToUpdate.UpdateAmbientLight();
                    sectorToUpdate.insideInvalidateLightQueue = false;
                }

                updateRounds++;
            }
        }

        static private int CompareSectorsByDistanceToPlayer(Sector sector1, Sector sector2)
        {
            int dist1 = (currentPlayerPositionForSort - sector1.tileOffset).GetDistanceSquared();
            int dist2 = (currentPlayerPositionForSort - sector2.tileOffset).GetDistanceSquared();

            return dist2.CompareTo(dist1);
        }


        //更新所有light
        public void UpdateAllTilesLight()
        {
            for (int x = 0; x < xSectors; x++)
                for (int z = 0; z < zSectors; z++)
                    for (int y = ySectors - 1; y >= 0; y--)
                        EnqueueInvalidatedLight(x, y, z);
        }

        public void Clear()
        {
            world = null;

            pendingSectorsUpdate = null;
            pendingSectorsUpdateLight = null;

            foreach (Sector s in sectors)
                s.Clear();

            sectors = null;
        }
    }
}